﻿Imports System.Reflection
Imports NotBasic.Compiler.Semantic

Namespace Compiler

    ''' <summary>
    ''' Manage extern referenced Assemblies. When requesting a symbol, the compiler will search referenced list for this symbol,
    ''' and then add a <c>ReferencedTypeDescription</c> to the symbol table.
    ''' </summary>
    ''' <remarks>The mscorlib.dll is by default consider referenced.</remarks>
    Public Class ReferenceManager
        Implements IDisposable

        Private Const c_corlibAssemblyName As String = "mscorlib.dll"

        Private m_asmList As List(Of ReferencedAssembly)
        Private m_referenceAsmHost As AppDomain
        Private m_mscorlib As ReferencedAssembly

        Public Sub New()
            m_asmList = New List(Of ReferencedAssembly)
            m_referenceAsmHost = AppDomain.CreateDomain("ReferenceManagedHostDomain" & Me.GetHashCode(), Nothing)

            'Reference the mscorlib.dll
            'm_asmList.Add(New ReferencedAssembly(m_referenceAsmHost.Load(c_corlibAssemblyName)))
        End Sub

        Public Sub AddReference(ByVal asmName As String, ByVal externAlias As Symbol)
            CheckDisposed()

            m_asmList.Add(New ReferencedAssembly(m_referenceAsmHost.Load(asmName), externAlias))
        End Sub

        Public Sub AddReference(ByVal asmName As String)
            CheckDisposed()

            m_asmList.Add(New ReferencedAssembly(m_referenceAsmHost.Load(asmName)))
        End Sub

        Public Function TryFindTypeDefinition(ByVal Name As Symbol, ByVal qualifier As Qualifier, ByVal externAlias As Symbol) As IList(Of Type)
            'Get full name
            Dim fullName As String = qualifier.ToString & Name.ToString
            Dim resultList As New List(Of Type)

            If externAlias Is Nothing OrElse String.IsNullOrEmpty(externAlias.Name) Then
                externAlias = ReferencedAssembly.GlobalAlias
            End If

            For Each refAsm In m_asmList
                If refAsm.ExternAlias.Equals(externAlias) Then
                    Debug.Assert(refAsm.Assembly IsNot Nothing, "The referenced assembly should not be nothing")
                    Dim modules = refAsm.Assembly.GetModules()

                    For Each asmModule In modules
                        Dim result = asmModule.GetType(fullName, True)
                        If result IsNot Nothing Then
                            resultList.Add(result)
                        End If
                    Next
                End If
            Next

            Return resultList
        End Function

        Private Sub CheckDisposed()
            If disposedValue Then
                Throw New ObjectDisposedException(Me.GetType.Name)
            End If
        End Sub


        Private disposedValue As Boolean = False        ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ' TODO: free other state (managed objects).

                    If m_referenceAsmHost IsNot Nothing Then
                        AppDomain.Unload(m_referenceAsmHost)
                        m_asmList = Nothing
                        m_referenceAsmHost = Nothing
                    End If
                End If

                ' TODO: free your own state (unmanaged objects).
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub

#Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

        Public ReadOnly Property Mscorlib() As ReferencedAssembly
            Get
                If m_mscorlib Is Nothing Then
                    'find mscorlib
                    m_mscorlib = m_asmList.Find(Function(asm) asm.Assembly.ManifestModule.Name = "mscorlib.dll")
                End If

                Return m_mscorlib
            End Get
        End Property
    End Class

    Public Class ReferencedAssembly

        Private Const c_globalAlias As String = "Global"
        Public Shared ReadOnly GlobalAlias As Symbol = Symbol.Get(c_globalAlias)

        Public Sub New(ByVal asm As Assembly, ByVal externAlias As Symbol)
            m_externAlias = ExternAlias
            m_assmbly = asm
        End Sub

        Public Sub New(ByVal asm As Assembly)
            MyClass.New(asm, Symbol.Get(c_globalAlias))
        End Sub

        Private m_externAlias As Semantic.Symbol
        Public ReadOnly Property ExternAlias() As Symbol
            Get
                Return m_externAlias
            End Get
        End Property

        Private m_assmbly As Assembly
        Public ReadOnly Property Assembly() As Assembly
            Get
                Return m_assmbly
            End Get
        End Property

    End Class
End Namespace
